package com.lagou.sqlSession;

import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.pojo.SqlCommandType;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }


    @Override
    public <E> List<E> selectList(String statementid, Object param) throws Exception {

        //将要去完成对simpleExecutor里的query方法的调用
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);
        List<Object> list = executor.query(mappedStatement, param);

        return (List<E>) list;
    }

    @Override
    public <T> T selectOne(String statementid, Object param) throws Exception {
        List<Object> objects = selectList(statementid, param);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或者返回结果过多");
        }

    }

    @Override
    public int insert(String statement, Object parameter) throws Exception {
        return doUpdate(statement,parameter);
    }

    @Override
    public int update(String statement, Object parameter) throws Exception {
        return doUpdate(statement,parameter);
    }

    @Override
    public int delete(String statement, Object parameter) throws Exception {
        return doUpdate(statement,parameter);
    }

    @Override
    public void commit() throws SQLException {
        executor.commit();
    }

    @Override
    public void rollback() throws SQLException {
        executor.rollback();
    }

    @Override
    public void close() throws SQLException {
        executor.close();
    }

    private int doUpdate(String statement, Object parameter) throws Exception {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statement);
        return executor.update(mappedStatement,parameter);
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用JDK动态代理来为Dao接口生成代理对象，并返回

        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底层都还是去执行JDBC代码 //根据不同情况，来调用selctList或者selectOne
                // 准备参数 1：statmentid :sql语句的唯一标识：namespace.id= 接口全限定名.方法名
                String statementId = getStatmentId(method);

                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                if (mappedStatement == null) {
                    throw new RuntimeException(statementId + "没有对应的配置信息");
                }

                SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
                switch (sqlCommandType) {
                    case SELECT:
                        // 准备参数2：params:args
                        // 获取被调用方法的返回值类型
                        Type genericReturnType = method.getGenericReturnType();
                        // 判断是否进行了 泛型类型参数化
                        if (genericReturnType instanceof ParameterizedType) {
                            List<Object> objects = DefaultSqlSession.this.selectList(statementId, args[0]);
                            return objects;
                        }

                        return DefaultSqlSession.this.selectOne(statementId, args[0]);
                    case DELETE:
                        return rowCountResult(method,DefaultSqlSession.this.delete(statementId,args[0]));
                    case INSERT:
                        return rowCountResult(method,DefaultSqlSession.this.insert(statementId,args[0]));
                    case UPDATE:
                        return rowCountResult(method,DefaultSqlSession.this.update(statementId,args[0]));
                    default:
                        throw new RuntimeException("不能执行方法: " + statementId);
                }
            }
        });

        return (T) proxyInstance;
    }

    private Object rowCountResult(Method method,int rowCount) {
        final Object result;
        if (void.class.equals(method.getReturnType())) {
            result = null;
        } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
            result = rowCount;
        } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
            result = (long)rowCount;
        } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
            result = rowCount > 0;
        } else {
            throw new RuntimeException("方法 '" + getStatmentId(method) + "' 的返回类型不支持: " + method.getReturnType());
        }
        return result;
    }

    private String getStatmentId(Method method){
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();

        String statementId = className + "." + methodName;
        return statementId;
    }
}
